package com.eric.study.device;

import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.PostStop;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;
import com.eric.study.message.Message;
import org.slf4j.Logger;

import java.util.Optional;

/**
 * <p>
 * Device
 *
 * <p>
 * change history:
 *
 * <pre>
 * date         defect       person          comments
 * --------------------------------------------------------------------------------
 * 2023/8/19    ********     Eric    create file.
 * </pre>
 *
 * @author Eric
 * @date 2023/8/19 14:02
 * @since 1.0.0
 */
public class Device extends AbstractBehavior<Device.DeviceMessage> {

    /**
     * 设备所在的 Group 编号
     */
    private final String groupId;

    /**
     * 设备编号
     */
    private final String deviceId;

    /**
     * 日志记录工具
     */
    private final Logger log;

    /**
     * 温度记录
     */
    private Optional<Double> lastTemperatureReading = Optional.empty();


    private Device(ActorContext<DeviceMessage> context, String groupId, String deviceId) {
        super(context);
        this.groupId = groupId;
        this.deviceId = deviceId;
        this.log = context.getLog();

        log.info("Device actor {}-{} started. path: {}", groupId, deviceId, context.getSelf());
    }

    public static Behavior<DeviceMessage> create(String groupId, String deviceId) {
        return Behaviors.setup(context -> new Device(context, groupId, deviceId));
    }

    @Override
    public Receive<DeviceMessage> createReceive() {
        return newReceiveBuilder()
                // 读温度命令处理
                .onMessage(ReadTemperature.class, msg -> {
                    msg.replyTo.tell(new RespondTemperature(msg.requestId(),
                            deviceId,
                            lastTemperatureReading
                    ));
                    return this;
                })
                // 写温度命令处理
                .onMessage(RecordTemperature.class, msg -> {
                    log.info("Recorded temperature reading {} with {}", msg.value(), msg.requestId());
                    lastTemperatureReading = Optional.of(msg.value());
                    msg.replyTo.tell(new TemperatureRecorded(msg.requestId()));
                    return this;
                })
                // 关闭 Actor 命令
                .onMessage(Passivate.class, msg -> Behaviors.stopped())
                // actor 停止信号监听
                .onSignal(PostStop.class, signal -> {
                    log.warn("Device actor {}-{} stopped.", groupId, deviceId);
                    return this;
                })
                .build();
    }

    public interface DeviceMessage extends Message {
    }

    /**
     * 获取温度命令
     *
     * @param replyTo 消息回复者的 Actor 引用
     */
    public record ReadTemperature(long requestId, ActorRef<RespondTemperature> replyTo) implements DeviceMessage {
    }

    /**
     * 响应结果
     *
     * @param value     温度值
     * @param deviceId  当前设备编号
     * @param requestId 请求编号
     */
    public record RespondTemperature(long requestId,
                                     String deviceId,
                                     Optional<Double> value
    ) implements DeviceMessage {
    }

    /**
     * 记录温度命令
     *
     * @param requestId 请求编号
     * @param value     温度值
     * @param replyTo   温度记录成功后的响应消息
     */
    public record RecordTemperature(long requestId, double value,
                                    ActorRef<TemperatureRecorded> replyTo) implements DeviceMessage {
    }

    /**
     * 温度记录成功后的响应结果
     *
     * @param requestId 请求编号
     */
    public record TemperatureRecorded(long requestId) {
    }

    /**
     * 终止设备 Actor 命令
     */
    public enum Passivate implements DeviceMessage {
        INSTANCE
    }


}
